<template>
  <view class="waterfalls-flow">
    <view v-for="(item, index) in data.column" :key="index" class="waterfalls-flow-column"
      :id="`waterfalls_flow_column_${index + 1}`" :msg="msg" :style="{ 'width': w, 'margin-left': index == 0 ? 0 : m }">
      <view :class="['column-value', { 'column-value-show': item2.o }]" v-for="(item2, index2) in columnValue(index)"
        :key="index2" :style="[s1]" @click.stop="wapperClick(item2)">
        <view class="inner" v-if="data.seat == 1">
          <!-- #ifdef  -->
          <!-- #ifdef VUE2 -->
          <slot name="slot{{item2.index}}"></slot>
          <!-- #endif -->
          <!-- #ifdef VUE3 -->
          <slot :name="`slot${item2.index}`"></slot>
          <!-- #endif -->
          <!-- #endif -->
          <!-- #ifndef  -->
          <!-- <slot :bind="item2"></slot> -->
          <!-- #endif -->
        </view>
        <image
          :class="['img', { 'img-hide': item2[hideImageKey] == true || item2[hideImageKey] == 1 }, { 'img-error': !item2[data.imageKey] }]"
          :src="item2[data.imageKey]" mode="widthFix" @load="imgLoad(item2, index + 1)"
          @error="imgError(item2, index + 1)" @click.stop="imageClick(item2)"></image>
        <view class="inner" v-if="data.seat == 2">
          <!-- #ifdef  -->
          <!-- #ifdef VUE2 -->
          <slot name="slot{{item2.index}}"></slot>
          <!-- #endif -->
          <!-- #ifdef VUE3 -->
          <slot :name="`slot${item2.index}`"></slot>
          <!-- #endif -->
          <!-- #endif -->
          <!-- #ifndef  -->
          <!-- <slot v-bind="item2"></slot> -->
          <!-- #endif -->
        </view>
      </view>
    </view>
  </view>
</template>
<script>
export default {
  props: {
    value: Array,
    column: { // 列的数量 
      type: [String, Number],
      default: 2
    },
    maxColumn: { // 最大列数 
      type: [String, Number],
      default: 5
    },
    columnSpace: { // 列之间的间距 百分比
      type: [String, Number],
      default: 2
    },
    imageKey: { // 图片key
      type: [String],
      default: 'image'
    },
    hideImageKey: { // 隐藏图片key
      type: [String],
      default: 'hide'
    },
    seat: { // 文本的位置，1图片之上 2图片之下
      type: [String, Number],
      default: 2
    },
    listStyle: { // 单个展示项的样式：eg:{'background':'red'}
      type: Object
    }
  },
  data() {
    return {
      data: {
        list: this.value ? this.value : [],
        column: this.column < 2 ? 2 : this.column,
        columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
        imageKey: this.imageKey,
        seat: this.seat
      },
      msg: 0,
      listInitStyle: {
        'border-radius': '12rpx',
        'margin-bottom': '20rpx',
        'background-color': '#F7F7F7'
      },
      adds: [], //预置数据
      isLoaded: true,
      curIndex: 0,
      isRefresh: true,
      flag: false,
      refreshDatas: []
    }
  },
  computed: {
    // 计算列宽
    w() {
      const column_rate = `${100 / this.data.column - (+this.data.columnSpace)}%`;
      return column_rate;
    },
    // 计算margin
    m() {
      const column_margin = `${(100 - (100 / this.data.column - (+this.data.columnSpace)).toFixed(5) * this.data.column) / (this.data.column - 1)}%`;
      return column_margin;
    },
    // list样式
    s1() {
      return { ...this.listInitStyle, ...this.listStyle };
    }
  },
  created() {
    // 初始化
    this.refresh();
  },
  methods: {
    // 预加载图片
    loadImages(idx = 0) {
      let count = 0;
      const newList = this.data.list.filter((item, index) => index >= idx);
      for (let i = 0; i < newList.length; i++) {
        // #ifndef APP-PLUS
        uni.getImageInfo({
          src: `${newList[i][this.imageKey]}.jpg`,
          complete: res => {
            count++;
            if (count == newList.length) this.initValue(idx);
          }
        })
        // #endif
        // #ifdef APP-PLUS
        plus.io.getImageInfo({
          src: `${newList[i][this.imageKey]}.jpg`,
          complete: res => {
            count++;
            if (count == newList.length) this.initValue(idx);
          }
        })
        // #endif
      }
    },
    // 刷新
    refresh() {
      if (!this.isLoaded) {
        this.refreshDatas = this.value;
        return false;
      };
      setTimeout(() => {
        this.refreshDatas = [];
        this.isRefresh = true;
        this.adds = [];
        this.data.list = this.value ? this.value : [];
        this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
        this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
        this.data.imageKey = this.imageKey;
        this.data.seat = this.seat;
        this.curIndex = 0;
        // 每列的数据初始化
        for (let i = 1; i <= this.data.column; i++) {
          this.data[`column_${i}_values`] = [];
          this.msg++;
        }
        this.$nextTick(() => {
          this.initValue(this.curIndex, 'refresh==>');
        })
      }, 1)
    },
    columnValue(index) {
      return this.data[`column_${index + 1}_values`];
    },
    change(newValue) {
      for (let i = 0; i < this.data.list.length; i++) {
        const cv = this.data[`column_${this.data.list[i].column}_values`];
        for (let j = 0; j < cv.length; j++) {
          if (newValue[i] && i === cv[j].index) {
            this.data[`column_${this.data.list[i].column}_values`][j] = Object.assign(cv[j], newValue[i]);
            this.msg++;
            break;
          }
        }
      }
    },
    getMin(a, s) {
      let m = a[0][s];
      let mo = a[0];
      for (var i = a.length - 1; i >= 0; i--) {
        if (a[i][s] < m) {
          m = a[i][s];
        }
      }
      mo = a.filter(i => i[s] == m);
      return mo[0];
    },
    // 计算每列的高度
    getMinColumnHeight() {
      return new Promise(resolve => {
        const heightArr = [];
        for (let i = 1; i <= this.data.column; i++) {
          const query = uni.createSelectorQuery().in(this);
          query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
            heightArr.push({ column: i, height: data.height });
          }).exec(() => {
            if (this.data.column <= heightArr.length) {
              resolve(this.getMin(heightArr, 'height'));
            }
          });
        }
      })
    },
    async initValue(i, from) {
      this.isLoaded = false;
      if (i >= this.data.list.length || this.refreshDatas.length) {
        this.msg++;
        this.loaded();
        return false;
      }
      const minHeightRes = await this.getMinColumnHeight();
      const c = this.data[`column_${minHeightRes.column}_values`];
      this.data.list[i].column = minHeightRes.column;
      c.push({ ...this.data.list[i], cIndex: c.length, index: i, o: 0 });
      this.msg++;
    },
    // 图片加载完成
    imgLoad(item, c) {
      const i = item.index;
      item.o = 1;
      this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
      this.initValue(i + 1);
    },
    // 图片加载失败
    imgError(item, c) {
      const i = item.index;
      item.o = 1;
      item[this.data.imageKey] = null;
      this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
      this.initValue(i + 1);
    },
    // 渲染结束
    loaded() {
      if (this.refreshDatas.length) {
        this.isLoaded = true;
        this.refresh();
        return false;
      }
      this.curIndex = this.data.list.length;
      if (this.adds.length) {
        this.data.list = this.adds[0];
        this.adds.splice(0, 1);
        this.initValue(this.curIndex);
      } else {
        if (this.data.list.length) this.$emit('loaded');
        this.isLoaded = true;
        this.isRefresh = false;
      }
    },
    // 单项点击事件
    wapperClick(item) {
      this.$emit('wapperClick', item);
    },
    // 图片点击事件
    imageClick(item) {
      this.$emit('imageClick', item);
    }
  },
  watch: {
    value: {
      deep: true,
      handler(newValue, oldValue) {
        setTimeout(() => {
          this.$nextTick(() => {
            if (this.isRefresh) return false;
            if (this.isLoaded) {
              // if (newValue.length <= this.curIndex) return this.refresh();
              if (newValue.length <= this.curIndex) return this.change(newValue);
              this.data.list = newValue;
              this.$nextTick(() => {
                this.initValue(this.curIndex, 'watch==>');
              })
            } else {
              this.adds.push(newValue);
            }
          })
        }, 10)
      }
    },
    column(newValue) {
      this.refresh();
    }
  }
}
</script>
<style lang="scss" scoped>
.waterfalls-flow {
  overflow: hidden;

  &-column {
    float: left;
  }
}

.column-value {
  width: 100%;
  font-size: 0;
  overflow: hidden;
  transition: opacity .4s;
  opacity: 0;

  &-show {
    opacity: 1;
  }

  .inner {
    font-size: 30rpx;
  }

  .img {
    width: 100%;

    &-hide {
      display: none;
    }

    &-error {
      background: #f2f2f2 url() no-repeat center center;
    }
  }
}
</style>
